use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use uuid::Uuid;

/// Unique identifier for nodes in the code graph
pub type NodeId = Uuid;

/// Unique identifier for edges in the code graph
pub type EdgeId = Uuid;

/// Unique identifier for snapshots
pub type SnapshotId = Uuid;

/// Types of nodes in the code graph
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum NodeType {
    Function,
    Class,
    Interface,
    Struct,
    Enum,
    Variable,
    Constant,
    Import,
    Export,
    Module,
    Package,
    Namespace,
    Call,
    Reference,
    Assignment,
    Declaration,
    Comment,
    Unknown,
}

impl std::fmt::Display for NodeType {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            NodeType::Function => write!(f, "function"),
            NodeType::Class => write!(f, "class"),
            NodeType::Interface => write!(f, "interface"),
            NodeType::Struct => write!(f, "struct"),
            NodeType::Enum => write!(f, "enum"),
            NodeType::Variable => write!(f, "variable"),
            NodeType::Constant => write!(f, "constant"),
            NodeType::Import => write!(f, "import"),
            NodeType::Export => write!(f, "export"),
            NodeType::Module => write!(f, "module"),
            NodeType::Package => write!(f, "package"),
            NodeType::Namespace => write!(f, "namespace"),
            NodeType::Call => write!(f, "call"),
            NodeType::Reference => write!(f, "reference"),
            NodeType::Assignment => write!(f, "assignment"),
            NodeType::Declaration => write!(f, "declaration"),
            NodeType::Comment => write!(f, "comment"),
            NodeType::Unknown => write!(f, "unknown"),
        }
    }
}

/// Types of edges in the code graph
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum EdgeType {
    /// Function calls another function
    Calls,
    /// Class inherits from another class
    Inherits,
    /// Class implements an interface
    Implements,
    /// Module imports another module
    Imports,
    /// Variable references another symbol
    References,
    /// Function defines a variable
    Defines,
    /// Class contains a method
    Contains,
    /// Function depends on another function
    DependsOn,
    /// Generic dependency relationship
    Uses,
    /// Control flow relationship
    ControlFlow,
    /// Data flow relationship
    DataFlow,
    /// Type relationship
    TypeRelation,
    /// Unknown relationship
    Unknown,
}

/// Source location information
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SourceLocation {
    pub file_path: String,
    pub start_line: u32,
    pub start_column: u32,
    pub end_line: u32,
    pub end_column: u32,
    pub byte_offset: u32,
    pub byte_length: u32,
}

impl SourceLocation {
    pub fn new(
        file_path: String,
        start_line: u32,
        start_column: u32,
        end_line: u32,
        end_column: u32,
    ) -> Self {
        Self {
            file_path,
            start_line,
            start_column,
            end_line,
            end_column,
            byte_offset: 0,
            byte_length: 0,
        }
    }

    pub fn with_bytes(mut self, offset: u32, length: u32) -> Self {
        self.byte_offset = offset;
        self.byte_length = length;
        self
    }
}

/// Metadata associated with nodes
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NodeMetadata {
    pub visibility: Option<String>,
    pub modifiers: Vec<String>,
    pub documentation: Option<String>,
    pub annotations: Vec<String>,
    pub complexity: Option<u32>,
    pub custom: HashMap<String, serde_json::Value>,
}

impl Default for NodeMetadata {
    fn default() -> Self {
        Self {
            visibility: None,
            modifiers: Vec::new(),
            documentation: None,
            annotations: Vec::new(),
            complexity: None,
            custom: HashMap::new(),
        }
    }
}

/// Metadata associated with edges
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EdgeMetadata {
    pub weight: f64,
    pub confidence: f64,
    pub context: Option<String>,
    pub custom: HashMap<String, serde_json::Value>,
}

impl Default for EdgeMetadata {
    fn default() -> Self {
        Self {
            weight: 1.0,
            confidence: 1.0,
            context: None,
            custom: HashMap::new(),
        }
    }
}

/// Text edit operation for incremental parsing
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TextEdit {
    pub start_byte: u32,
    pub old_end_byte: u32,
    pub new_end_byte: u32,
    pub start_position: Position,
    pub old_end_position: Position,
    pub new_end_position: Position,
    pub new_text: String,
}

/// Position in source code
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct Position {
    pub row: u32,
    pub column: u32,
}

impl Position {
    pub fn new(row: u32, column: u32) -> Self {
        Self { row, column }
    }
}

/// Code change information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CodeChange {
    pub file_path: String,
    pub language: String,
    pub edit: TextEdit,
    pub timestamp: chrono::DateTime<chrono::Utc>,
}

/// Result of a graph update operation
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GraphUpdateResult {
    pub success: bool,
    pub added_nodes: Vec<NodeId>,
    pub removed_nodes: Vec<NodeId>,
    pub modified_nodes: Vec<NodeId>,
    pub added_edges: Vec<EdgeId>,
    pub removed_edges: Vec<EdgeId>,
    pub modified_edges: Vec<EdgeId>,
    pub duration_ms: u64,
    pub errors: Vec<String>,
}

impl Default for GraphUpdateResult {
    fn default() -> Self {
        Self {
            success: true,
            added_nodes: Vec::new(),
            removed_nodes: Vec::new(),
            modified_nodes: Vec::new(),
            added_edges: Vec::new(),
            removed_edges: Vec::new(),
            modified_edges: Vec::new(),
            duration_ms: 0,
            errors: Vec::new(),
        }
    }
}

/// Node differences for incremental updates
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NodeDifference {
    pub added: Vec<NodeId>,
    pub removed: Vec<NodeId>,
    pub modified: Vec<NodeId>,
}

/// Query result from graph operations
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QueryResult {
    pub nodes: Vec<NodeId>,
    pub edges: Vec<EdgeId>,
    pub metadata: HashMap<String, serde_json::Value>,
    pub execution_time_ms: u64,
}

/// Performance metrics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PerformanceMetrics {
    pub parse_time_ms: u64,
    pub graph_update_time_ms: u64,
    pub memory_usage_bytes: u64,
    pub node_count: u64,
    pub edge_count: u64,
    pub error_count: u64,
}

/// Configuration for the graph system
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GraphConfig {
    pub max_cache_size: usize,
    pub cleanup_threshold: f64,
    pub max_concurrent_parsers: usize,
    pub storage_path: Option<String>,
    pub supported_languages: Vec<String>,
    pub enable_incremental: bool,
    pub enable_versioning: bool,
}

impl Default for GraphConfig {
    fn default() -> Self {
        Self {
            max_cache_size: 1000,
            cleanup_threshold: 0.8,
            max_concurrent_parsers: 4,
            storage_path: None,
            supported_languages: vec![
                "rust".to_string(),
                "python".to_string(),
                "javascript".to_string(),
                "typescript".to_string(),
                "java".to_string(),
                "cpp".to_string(),
                "c".to_string(),
                "go".to_string(),
            ],
            enable_incremental: true,
            enable_versioning: true,
        }
    }
}
